home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / IBPalettes / WW3DKit / WW3DLight.m < prev    next >
Encoding:
Text File  |  1995-03-22  |  45.0 KB  |  1,317 lines

  1. // copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
  2. // see COPYRIGHT for reuse legalities
  3. //
  4.  
  5.  
  6. #import "WW3DLight.h"
  7. #import "EveCommand.h"
  8. #import "WW3DAttributeState.h"
  9. #import "usefulWW3DFunctions.h"
  10.  
  11. @implementation WW3DLight
  12.  
  13.  
  14. #define wwPI          (3.1415926535897932384626433)
  15. #define DtoR         (wwPI/180.0)
  16. #define RtoD         (180.0/wwPI)
  17. #define    toDegrees(r)    ((r)*RtoD)
  18. #define    toRadians(d)    ((d)*DtoR)
  19. #define X_AXIS      1.0, 0.0, 0.0
  20. #define Y_AXIS      0.0, 1.0, 0.0
  21. #define Z_AXIS      0.0, 0.0, 1.0
  22.  
  23.  
  24. + initialize {  [WW3DLight setVersion:1]; return self; }
  25.  
  26. - init
  27. {
  28.   [super init];
  29.   visibleSizeScale = 1.0;
  30.   visibleWhenPrinting = NO;
  31.  
  32.   xColor[0] = 1.0;
  33.   xColor[1] = 0.0;
  34.   xColor[2] = 0.0;
  35.   yColor[0] = 0.0;
  36.   yColor[1] = 1.0;
  37.   yColor[2] = 0.0;
  38.   zColor[0] = 0.0;
  39.   zColor[1] = 0.0;
  40.   zColor[2] = 1.0;
  41.  
  42.   innerConeOpacity[0] = 0.3;
  43.   innerConeOpacity[1] = 0.3;
  44.   innerConeOpacity[2] = 0.3;
  45.   outerConeOpacity[0] = 0.1;
  46.   outerConeOpacity[1] = 0.1;
  47.   outerConeOpacity[2] = 0.1;
  48.   beamOpacity[0] = intensity;
  49.   beamOpacity[1] = intensity;
  50.   beamOpacity[2] = intensity;
  51.  
  52.   xRotate = 0.0;
  53.   yRotate = 0.0;
  54.  
  55.   NXConvertColorToRGB(color, &myRtColor[0], &myRtColor[1], &myRtColor[2]); 
  56.  
  57.   return self;
  58. }
  59.  
  60. - awake
  61. {
  62.   [super awake];
  63.   NXConvertColorToRGB(color, &myRtColor[0], &myRtColor[1], &myRtColor[2]); 
  64.   return self;
  65. }
  66.  
  67. - copyFromZone:(NXZone *)zone  {  return [super copyFromZone:zone]; }
  68.  
  69. - synchUpRotationValues
  70. {
  71.   v[0] = from[0] - to[0];
  72.   v[1] = from[1] - to[1];
  73.   v[2] = from[2] - to[2];
  74.   vMagnitude = sqrt((double)((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2])));
  75.  
  76.   u[0] = v[0]/vMagnitude;
  77.   u[1] = v[1]/vMagnitude;
  78.   u[2] = v[2]/vMagnitude;
  79.  
  80.   if (u[2] != 0.0)
  81.   {  yRotate = toDegrees(((RtFloat)atan((double)(u[0]/u[2]))));
  82.      xRotate = toDegrees(( -1. * (RtFloat)atan((double)(u[1]/u[2]))));
  83.   }
  84.   else
  85.   {  yRotate = toDegrees(((RtFloat)atan((double)(u[0]))));
  86.      xRotate = toDegrees((-1. * (RtFloat)atan((double)(u[1]))));
  87.   }
  88.  
  89. //  NXLogError("from == (%f, %f, %f) : to== (%f, %f, %f)\n", 
  90. //             from[0], from[1], from[2], to[0], to[1], to[2]);
  91. //  NXLogError("u == (%f, %f, %f) : xRotate == %f : yRotate == %f\n", 
  92. //             u[0], u[1], u[2], xRotate, yRotate);
  93.  
  94.   return self;
  95. }
  96.  
  97. - (BOOL)on  {  return lightFlags.on;  }
  98.  
  99. - setColor:(NXColor)aColor
  100. {
  101.   [super setColor:aColor];
  102.   NXConvertColorToRGB(color, &myRtColor[0], &myRtColor[1], &myRtColor[2]); 
  103.   return self;
  104. }
  105.  
  106. - ribCommands { return ribCommandList; }
  107. - appendRIBCommand:newRIBCommand
  108. {
  109.     if ([newRIBCommand isKindOf:[EveCommand class]]) {
  110.     hasEveCmd = YES;
  111.     }
  112.     [ribCommandList addObject:newRIBCommand];
  113.     dirtyBoundingBox = YES;
  114.     return self;
  115. }
  116.  
  117.  
  118. - renderSelfAsBox:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  119. {
  120.   int      i, howMany = [ribCommandList count];
  121.  
  122.  
  123.   [super renderSelf:camera];
  124.  
  125.   for (i = 0; i < howMany; i++)
  126.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderSelfAsBox:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  127.   }
  128.  
  129.   return self;
  130. }
  131.  
  132. - renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  133. {
  134.   RtFloat  length;
  135.   int      i, howMany = [ribCommandList count];
  136.  
  137.  
  138.   [super renderSelf:camera];
  139.  
  140.  
  141.   // uncomment this eventually, otherwise we'll always render the lights...
  142.   //if ((NXDrawingStatus != NX_DRAWING) && !visibleWhenPrinting)
  143.   //{  return self;
  144.   //}
  145.  
  146.   RiAttributeBegin(); // this geometry and state that we don't really
  147.                       // want to foist on the light shader that this shape 
  148.                       // actually represents, so we wrap all the following 
  149.                       // in an AttributeBegin/End block.
  150.  
  151.   RiSurface("matte", RI_NULL);  // force a reasonable, cheap shader
  152.   // need to know if it's selected or not - if it is, draw as selected color otherwise use own color
  153.   // for now, just color it...
  154.   RiColor(myRtColor);
  155.  
  156.    switch (type)  
  157.    {  case N3D_AmbientLight:  // an ambient light is just a colored sphere of diameter 1
  158.                               RiSphere(visibleSizeScale * .5, visibleSizeScale * -.5, visibleSizeScale * .5, 360.0, RI_NULL);
  159.                               break;
  160.       case N3D_PointLight:    // a point light is six pointed star
  161.                               RiTranslate(from[0], from[1], from[2]);
  162.                               RiSphere(visibleSizeScale * .25, visibleSizeScale * -.25, visibleSizeScale * .25, 360.0, RI_NULL);
  163.  
  164.                               RiColor(zColor);
  165.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  166.                               RiTranslate(0, 0, visibleSizeScale * .4);
  167.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  168.                               RiTranslate(0, 0, visibleSizeScale * -0.4);
  169.                               RiRotate(-90, X_AXIS);
  170.                               RiColor(yColor);
  171.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  172.                               RiTranslate(0, 0, visibleSizeScale * 0.4);
  173.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  174.                               RiTranslate(0, 0, visibleSizeScale * -0.4);
  175.                               RiRotate(-90, X_AXIS);
  176.                               RiColor(zColor);
  177.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  178.                               RiTranslate(0, 0, visibleSizeScale * 0.4);
  179.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  180.                               RiTranslate(0, 0, visibleSizeScale * -0.4);
  181.  
  182.                               RiRotate(-90, X_AXIS);
  183.                               RiColor(yColor);
  184.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  185.                               RiTranslate(0, 0, visibleSizeScale * 0.4);
  186.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  187.                               RiTranslate(0, 0, visibleSizeScale * -0.4);
  188.  
  189.                               RiRotate(-90, X_AXIS);
  190.                               RiRotate(90, Y_AXIS);
  191.                               RiColor(xColor);
  192.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  193.                               RiTranslate(0, 0, visibleSizeScale * 0.4);
  194.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  195.                               RiTranslate(0, 0, visibleSizeScale * -0.4);
  196.  
  197.                               RiRotate(180, Y_AXIS);
  198.                               RiColor(xColor);
  199.                               RiCylinder(visibleSizeScale * .05, 0, visibleSizeScale * .4, 360, RI_NULL);
  200.                               RiTranslate(0, 0, visibleSizeScale * 0.4);
  201.                               RiCone(visibleSizeScale * .1, visibleSizeScale * .1, 360, RI_NULL);
  202.                           break;
  203.       case N3D_DistantLight:  // this looks like a normalized arrow
  204.                               RiTranslate(from[0], from[1], from[2]);
  205.                               RiRotate(yRotate, Y_AXIS);
  206.                               RiRotate(xRotate, X_AXIS);
  207.  
  208.                               RiRotate(180, Y_AXIS);  // flip around to point Z at toPoint
  209.                               RiCylinder(visibleSizeScale * .1, 0, visibleSizeScale * .8, 360, RI_NULL);
  210.                               RiTranslate(0, 0, visibleSizeScale * .8);
  211.                               RiCone(visibleSizeScale * .2, visibleSizeScale * .2, 360, RI_NULL);
  212.                               break;
  213.       case N3D_SpotLight:     // two cones and an arrow pointing out
  214.                               RiTranslate(from[0], from[1], from[2]);
  215.                               RiRotate(yRotate, Y_AXIS);
  216.                               RiRotate(xRotate, X_AXIS);
  217.                               RiTranslate(0.0, 0.0, -1.0 * visibleSizeScale);
  218.  
  219.                               RiOpacity(outerConeOpacity);
  220.                               RiCone(visibleSizeScale,  visibleSizeScale * tan((double)((wwPI/180.0) * coneAngle)), 360, RI_NULL);
  221.                               RiOpacity(innerConeOpacity);
  222.                               RiCone(visibleSizeScale,  visibleSizeScale * tan((double)((wwPI/180.0) * (coneAngle - coneDelta))), 360, RI_NULL);
  223.  
  224.                               // need to represent the beam distribution
  225.                               RiOpacity(beamOpacity);
  226.                               RiTranslate(0.0, 0.0, visibleSizeScale);
  227.                               RiRotate(180, Y_AXIS);  // flip around to point Z at toPoint
  228.                               length = (beamDistribution * visibleSizeScale * .8);
  229.                               RiCylinder(visibleSizeScale * .01, 0, length, 360, RI_NULL);
  230.                               RiTranslate(0, 0, length);
  231.                               RiCone(length * .2, length * .05, 360, RI_NULL);
  232.                               break;
  233.       default:                // complain
  234.                           break;
  235.   }
  236.   RiAttributeEnd();
  237.   
  238.   for (i = 0; i < howMany; i++)
  239.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  240.   }
  241.  
  242.   return self;
  243. }
  244.  
  245. - renderSelf:(WW3DCamera *)camera
  246. {
  247.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  248.            shutterCloseTime = [camera shutterCloseTime];
  249.  
  250.  
  251.   return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  252. }
  253.  
  254. - makePointFrom:(RtPoint)pf intensity:(RtFloat)i
  255. {
  256.   [super makePointFrom:pf intensity:i];
  257.   beamOpacity[0] = intensity;
  258.   beamOpacity[1] = intensity;
  259.   beamOpacity[2] = intensity;
  260.   return [self synchUpRotationValues];
  261. }
  262.  
  263. - makeDistantFrom:(RtPoint)pf to:(RtPoint)pt intensity:(RtFloat)i
  264. {
  265.   [super makeDistantFrom:pf to:pt intensity:i];
  266.   beamOpacity[0] = intensity;
  267.   beamOpacity[1] = intensity;
  268.   beamOpacity[2] = intensity;
  269.   return [self synchUpRotationValues];
  270. }
  271.  
  272. - makeSpotFrom:(RtPoint)pf to:(RtPoint)pt
  273.         coneAngle:(RtFloat)ca coneDelta:(RtFloat)cd
  274.         beamDistribution:(RtFloat)bd
  275.         intensity:(RtFloat)i
  276. {
  277.   [super makeSpotFrom:pf to:pt    coneAngle:ca coneDelta:cd beamDistribution:bd intensity:i];
  278.   beamOpacity[0] = intensity;
  279.   beamOpacity[1] = intensity;
  280.   beamOpacity[2] = intensity;
  281.   return [self synchUpRotationValues];
  282.    
  283. }
  284.  
  285.  
  286. - setFrom:(RtPoint)pf
  287. {
  288.   [super setFrom:pf];
  289.   return [self synchUpRotationValues];
  290. }
  291.  
  292.  
  293. - setFrom:(RtPoint)pf to:(RtPoint)pt
  294. {
  295.   [super setFrom:pf to:pt];
  296.   return [self synchUpRotationValues];
  297. }
  298.  
  299. - setIntensity:(RtFloat)i
  300. {
  301.   [super setIntensity:i];
  302.   beamOpacity[0] = intensity;
  303.   beamOpacity[1] = intensity;
  304.   beamOpacity[2] = intensity;
  305.   return [self synchUpRotationValues];
  306. }
  307.  
  308. - transformCTM:(WW3DAttributeState *)attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  309. {  NXLogError("why are you calling transformCTM:startingAt:endingAt: for WW3DShape <%s>?\n", [self shapeName]);
  310.    return self;
  311. }
  312.  
  313. - (BOOL)isSelectable { return YES; } // This is a N3DKit thing, not mine
  314.  
  315. - (BOOL)isLerpable { return NO; }
  316. - lerpWith:b by:(float)uValue { return self; }
  317. - lerpSelfWith:b by:(float)uValue { return self; }
  318.  
  319. - (BOOL)isMotionBlurrable { return YES; }  // this isn't strictly true, ya know...
  320.  
  321. - (BOOL)isCompoundCommand { return YES; } // this isn't strictly true, ya know...
  322.  
  323. - (BOOL)hasBoundingBox { return YES; }  // this isn't strictly true, ya know...
  324.  
  325. - (float)lastSampleIsAt
  326. {
  327.    float  oldestSample = 0.0, newSample;
  328.    int      i, howMany = [ribCommandList count];
  329.  
  330.  
  331.   for (i = 0; i < howMany; i++)
  332.   {  newSample = [(id <WWRenderable>)[ribCommandList objectAt:i] lastSampleIsAt];
  333.      if (newSample > oldestSample)
  334.      {  oldestSample = newSample;
  335.      }
  336.   }
  337.  
  338.   newSample = [descendant lastSampleIsAt];
  339.   if (newSample > oldestSample)
  340.   {  oldestSample = newSample;
  341.   }
  342.  
  343.   newSample = [nextPeer lastSampleIsAt];
  344.   if (newSample > oldestSample)
  345.   {  oldestSample = newSample;
  346.   }
  347.  
  348.   return oldestSample;
  349. }
  350.  
  351. - (unsigned long int)maxSampleBandwidth;
  352. {
  353.    unsigned long int  maxSampleBandwidth = 50;  //WAVE FIX ME
  354.    int      i, howMany = [ribCommandList count];
  355.  
  356.  
  357.   for (i = 0; i < howMany; i++)
  358.   {  maxSampleBandwidth += [(id <WWRenderable>)[ribCommandList objectAt:i] maxSampleBandwidth];
  359.   }
  360.  
  361.   maxSampleBandwidth += [descendant maxSampleBandwidth];
  362.   maxSampleBandwidth += [nextPeer maxSampleBandwidth];
  363.  
  364.   return maxSampleBandwidth;
  365. }
  366.  
  367.  
  368. - (BOOL)isMoot
  369. {
  370.   return NO;
  371. }
  372.  
  373. - (BOOL)isMootStartingAt:(float)startTime endingAt:(float)endTime  {  return [self isMoot];  }
  374.  
  375. - (BOOL)theSameAs:otherRIBCommand
  376. {
  377.   return NO;
  378. }
  379.  
  380. - (BOOL)similarTo:otherRIBCommand 
  381. {
  382.   if ([self class] != [otherRIBCommand class])
  383.   {  return NO;
  384.   }
  385.   return YES;
  386. }
  387.  
  388.  
  389. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime usingStream:(NXStream *)ns
  390. {
  391.   int      i, howMany = [ribCommandList count];
  392.  
  393.  
  394.   for (i = 0; i < howMany; i++)
  395.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  396.   }
  397.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  398.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  399.  
  400.   return self;
  401. }
  402.  
  403.  
  404. - renderMaps:(WW3DCamera *)camera usingStream:(NXStream *)ns
  405. {
  406.   int      i, howMany = [ribCommandList count];
  407.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  408.            shutterCloseTime = [camera shutterCloseTime];
  409.  
  410.  
  411.   for (i = 0; i < howMany; i++)
  412.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  413.   }
  414.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  415.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  416.  
  417.   return self;
  418. }
  419.  
  420.  
  421.  
  422. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  423. {
  424.   int      i, howMany = [ribCommandList count];
  425.  
  426.  
  427.   for (i = 0; i < howMany; i++)
  428.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  429.   }
  430.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  431.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  432.  
  433.   return self;
  434. }
  435.  
  436.  
  437. - renderMaps:(WW3DCamera *)camera
  438. {
  439.   int      i, howMany = [ribCommandList count];
  440.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  441.            shutterCloseTime = [camera shutterCloseTime];
  442.  
  443.  
  444.   for (i = 0; i < howMany; i++)
  445.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  446.   }
  447.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  448.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  449.  
  450.   return self;
  451. }
  452.  
  453.  
  454.  
  455.  
  456. - (BOOL)pushesOrPopsCTM { return NO; }
  457. - (BOOL)pushesCTM { return NO; }
  458. - (BOOL)popsCTM { return NO; }
  459.  
  460.  
  461. - addChild:newChild
  462. {
  463.   id  oldDescendant = nil,
  464.       oldDescendantList = [[List alloc] init],
  465.       parentShape = self;
  466.  
  467.  
  468.   // I think I want to unlink the old descendant, link the child in as
  469.   // the new one, and then make the oldDescendant the peer of the child.
  470.   oldDescendant = [parentShape descendant];
  471.   if (oldDescendant)
  472.   {  while (oldDescendant) 
  473.      {  [oldDescendantList addObject:oldDescendant];
  474.         [oldDescendant unlink];
  475.         oldDescendant = [parentShape descendant];
  476.      }
  477.   }
  478.   [parentShape linkDescendant:newChild];
  479.   if (oldDescendantList)
  480.   {  oldDescendant = [oldDescendantList objectAt:0];
  481.      while (oldDescendant) 
  482.      {  [newChild linkPeer:oldDescendant];
  483.         [oldDescendantList removeObjectAt:0];
  484.         oldDescendant = [oldDescendantList objectAt:0];
  485.      }
  486.      [oldDescendantList free];
  487.   }
  488.   dirtyBoundingBox = YES;
  489.  
  490.   return self;
  491. }
  492.  
  493.  
  494. - siblings
  495.   id  currentPeer;
  496.  
  497.  
  498.   // Really should cache this by trapping all the hierarchy frobbing and
  499.   // having a dirty bit set.  For right now, recalculate each time.
  500.  
  501.   [siblingList empty];
  502.  
  503.   if (self != [self firstPeer])
  504.   {  [siblingList addObject:[self firstPeer]];
  505.      currentPeer = [self firstPeer];
  506.  
  507.      while ([currentPeer nextPeer])
  508.      {  [siblingList addObject:[currentPeer nextPeer]];
  509.         currentPeer = [currentPeer nextPeer];
  510.      }
  511.   }
  512.  
  513.   return siblingList;
  514. }
  515.  
  516. - children 
  517.   id  currentPeer;
  518.  
  519.  
  520.   // Really should cache this by trapping all the hierarchy frobbing and
  521.   // having a dirty bit set.  For right now, recalculate each time.
  522.  
  523.   [childList empty];
  524.  
  525.   [childList addObject:[self descendant]];
  526.   currentPeer = descendant;
  527.   // note that the descendant is the firstPeer of it's siblings...
  528.   while ([currentPeer nextPeer])
  529.   {  [childList addObject:[currentPeer nextPeer]];
  530.      currentPeer = [currentPeer nextPeer];
  531.   }
  532.  
  533.   return childList;
  534. }
  535.  
  536. - parent { return ancestor; }
  537.  
  538. - (unsigned short)pathSeparator { return pathSeparator; }
  539. - setPathSeparator:(unsigned short)newSeparator { pathSeparator = newSeparator; return self; }
  540.  
  541.  
  542. - getChildGivenPath:(const char *)path
  543. {
  544.   char  *part = (char *)path;
  545.   int   cnt, howMany, i;
  546.   id    ret;
  547.  
  548.  
  549.   // the string looks something like "/foo/bar/zap/zow"
  550.   // if the whole string is "/", return yourself
  551.   // if the first part doesn't match your name, return nil.
  552.   // if what's left after the first part is nil, return self;
  553.   // if you're still here, send the message to each child until it doesn't return nil.  
  554.   // if you get through all your children and it still returns nil, return nil.
  555.  
  556.   if ((strlen(path) == 1) && ((unsigned short)(*part) == pathSeparator))
  557.   {  return self;
  558.   }
  559.  
  560.   while (*part && ((unsigned short)(*part) != pathSeparator)) { part++; } 
  561.   // from "/foo/bar" to "/bar" or from "/foo" to ""
  562.   cnt = part - (path + 1);
  563.   if (!cnt) {  return nil; } // catch the "/foo" to "" case
  564.  
  565.   if (!strncmp((path+1), [self shapeName], cnt))
  566.   {  // the first part is my name
  567.      if (!*part) { return self; }// if there isn't anything else, I'm who they're looking for
  568.  
  569.      // otherwise, send message to each child to see if it's them
  570.      howMany = [childList count];
  571.      i = 0;
  572.      while (i < howMany)
  573.      {  ret = [[childList objectAt:i] getChildGivenPath:part];
  574.         if (ret)
  575.         {  return ret;
  576.         }
  577.         i++;
  578.      }
  579.   }
  580.   // that isn't my name at the beginning - this isn't for me or any of my kids
  581.   return nil;
  582. }
  583.  
  584. - (char *)getPath
  585. {
  586.   char  *path, pathSeparatorString[2]; 
  587.   int   cnt = 1, 
  588.         i;
  589.   id    theShape;
  590.   List  *myShapeList;
  591.  
  592.  
  593.   myShapeList = [[List alloc] init];
  594.   theShape = self;
  595.   while (theShape)
  596.   {  cnt += 2 + strlen([theShape shapeName]);
  597.      [myShapeList addObject:theShape];
  598.      theShape = [theShape ancestor];
  599.   }
  600.  
  601.   path = malloc(cnt);
  602.   if (!path)
  603.   {  [myShapeList free];
  604.      return NULL;
  605.   }
  606.  
  607.   path[0] = '\0';
  608.   sprintf(pathSeparatorString, "%c", pathSeparator);
  609.   for (i = [myShapeList count] - 1; i >= 0; i--)  
  610.   {  strcat(path, pathSeparatorString);
  611.      strcat(path, [[myShapeList objectAt:i] shapeName]);
  612.   }
  613.   
  614.   return path;
  615. }
  616.  
  617. - preRenderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  618. {
  619.   int      i, howMany = [ribCommandList count];
  620.  
  621.  
  622.   for (i = 0; i < howMany; i++)
  623.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] preRenderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  624.   }
  625.   return self;
  626. }
  627.  
  628.  
  629. - preRenderSelf:(WW3DCamera *)camera
  630. {
  631.   int  i, howMany = [ribCommandList count];
  632.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  633.            shutterCloseTime = [camera shutterCloseTime];
  634.  
  635.  
  636.   for (i = 0; i < howMany; i++)
  637.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] preRenderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  638.   }
  639.   return self;
  640. }
  641.  
  642. - (RtBound *)boundingBoxStartingAt:(RtFloat)intervalStartTime endingAt:(RtFloat)intervalEndTime
  643.    if (dirtyBoundingBox) 
  644.    {  [self calculateBoundingBoxStartingAt:intervalStartTime endingAt:intervalEndTime];
  645.    }
  646.    return &boundingBox; 
  647. }
  648.  
  649.  
  650. - (int)findBoundingBoxFromRIBCommands:(int)startingIndex using:attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  651. {
  652.   RtBound   tmpBoundingBox;
  653.   RtMatrix  tmpCTM;
  654.   int       i = startingIndex,
  655.             incr,
  656.             howMany = [ribCommandList count];
  657.   id        cmd;
  658.   id        newAttributeState;
  659.  
  660.  
  661.   // first we need to find a bounding box to start with
  662.   while (i < howMany)
  663.   {  cmd = [ribCommandList objectAt:i];
  664.  
  665.      // if the cmd pops the attribute stack, we're done.
  666.      // we return how many commands we've gone through in our time here
  667.      if ([cmd popsCTM])
  668.      {  return (i - startingIndex);
  669.      }
  670.  
  671.      if ([cmd pushesCTM])  // here we go again...
  672.      {  newAttributeState = [[WW3DAttributeState alloc] init];
  673.         i++;
  674.         incr = [self findBoundingBoxFromRIBCommands:i using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  675.         // we now need to merge this sub-attribute block into our current one
  676.         if ([newAttributeState hasBoundingBox])
  677.         {  // This means that the bounding box in newAttributeState needs to be
  678.            // transformed by the ctm of attributeState, and then  we need to 
  679.            // "grow" to the current bound with that tranformed sub-bound.
  680.  
  681.            [attributeState getTransformMatrix:tmpCTM];
  682.           WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, [newAttributeState boundingBox], tmpCTM);
  683.           [attributeState growBoundingBox:&tmpBoundingBox];
  684.        }
  685.        else
  686.        {  NXLogError("warning: WW3DLight <%s> has a moot set of commands at indices %d to %d\n", [self shapeName], (i - 1), (i + incr - 1));
  687.           NXLogError("\tif there was a LightSource or AreaLight source in there, you can ignore this warning, though...\n");
  688.        }
  689.        i += incr;
  690.      }
  691.      else 
  692.      {  [cmd transformCTM:attributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  693.         i++;
  694.      }
  695.  
  696.     if ([cmd hasBoundingBox])
  697.     {  // great! we grow the bound of the current attribute state
  698.        [attributeState growBoundingBox:[cmd boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]];
  699.     }
  700.  
  701.   }
  702.   return (i - startingIndex);
  703. }
  704.  
  705.  
  706. - calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  707. {
  708.   RtBound   tmpBoundingBox;
  709.   RtMatrix  childMatrix, tmpCTM;
  710.   int       i, startingChildIndex = 0, howManyKids;
  711.   id        child, kids, newAttributeState;
  712.  
  713.  
  714.   // this one is tricky.  we'll need to be smart about this...
  715.  
  716.   // basically, each RIBCommand has it's own bounding box.  We ask
  717.   // each one in turn for it's boundingBox, and use that to build up ours.
  718.   // Remember that each command is smart enough to know if any of it's
  719.   // parameters have changed, but we need to be smart to realize if we have
  720.   // rotated/translated/scaled.  Recall that for scaling and translating we
  721.   // can just add that to the boundingBox without asking everyone to recalculate,
  722.   // but rotating mucks up everything.  
  723.  
  724.   // so we need to transform these points against our 4x4, right?
  725.   // Does the 3DKit take care of that for us?  Hmmm...
  726.  
  727.   // Actually, I don't think we need to cat our 4x4 against it, 
  728.   // although I'm still unclear...  Anyway, what we really want to do is 
  729.   // grovel over our ribCommandList and, to each object which answers YES
  730.   // to hasBoundingBox we send the appropriate msg to get the bounding box
  731.   // and compare it against ours.
  732.  
  733.   // actually, one more difficulty is that even though we might not
  734.   // have any geometry (i.e. have any RIBCommands that respond 
  735.   // positively to -hasBoundingBox), we still might have some commands
  736.   // transform the boundingBox, ie. do more than concatenate an identity 
  737.   // matrix against the matrix passed into -transform:.
  738.  
  739.   // what if you had 3 commands, the first two of which didn't have bounding boxes.
  740.   // first time through, i == 1 at the end
  741.   // next time, i == 2 at the end
  742.   // final time through, i doesn't get incremented, but we are done
  743.   // actually, it's more complicated than that.
  744.   // some of these RIBCommands might not have a bounding box, but they do transform the CTM
  745.   // also, you might have some transformational commands, then some geometry, then more
  746.   // transformational commands, then geometry, then some more transformational... These
  747.   // would all effect the children shapes in toto, but would also be affecting the 
  748.   // bounding box of the various geometry commands.
  749.   // urgh...
  750.  
  751.   // It's not really so bad.  Everything is taking place in the space of this shape, which 
  752.   // means that this shape's transformation matrix isn't consulted.  We start off with 
  753.   // the identity matrix, and transform a tmp CTM (current transformation matrix) as we
  754.   // move through the commands.
  755.  
  756.   // Actually, it's a bit trickier than you might think.  The problem
  757.   // is that although we have no fear of dropping down into a child until
  758.   // we get to the list of kids, we just might hit a RIBAttributeBegin,
  759.   // RIBTransformBegin, or RIBSolidBegin object, which would have the same effect
  760.   // So, it looks like we need to extend the WWRenderable protocol to let us know
  761.   // if the object pushes or pops the transformation stack.  We'll need to recurse
  762.   // down the transformation tree, building up the CTM, and transforming any bounding
  763.   // box we find.  We start off when the command pushes the stack.  We cons up a new
  764.   // CTM and set it to the Identity matrix.  We then start walking down the commands,
  765.   // first asking the command if it has a boundingBox.  If it does, we set our bound
  766.   // to it, and then transform the points in the bound by our CTM.  We
  767.   // then continue walking down the tree, asking each command if has a
  768.   // bounding box, and if does, transforming that box by the CTM, and then
  769.   // comparing it to our current bound, and growing it accordingly.  If the
  770.   // command doesn't have a bound, we just transform our CTM and our
  771.   // current bound by it.  We continue this until we get find a command
  772.   // which pops the attribute stack.  If, along the way, we hit a command which
  773.   // actually pushes the stack, we need to recurse down and call this routine again.
  774.   // The routine should be passed, and return, the id of a list of WW3DAttributeState
  775.   // objects, which have a bounding box, a CTM, etc. in them.
  776.  
  777.   newAttributeState = [[WW3DAttributeState alloc] init];
  778.   i = [self findBoundingBoxFromRIBCommands:0 using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  779.   if (i != [ribCommandList count])
  780.   {  NXLogError("warning: shape <%s> has an unbalanced attribute delimiter at command %d (expected %d)\n", 
  781.                 [self shapeName], i, [ribCommandList count]);
  782.      return nil;
  783.   }
  784.  
  785.   [newAttributeState getTransformMatrix:tmpCTM];
  786.  
  787.   if (![newAttributeState hasBoundingBox])  // this Shape has no real geometry in it, but it might have kids...
  788.   {  // we still need some approximation to start off with for this shape's bounding box.
  789.      // to get it, we use our first child's bounding box.
  790.      // if we don't have any kids, we just return.
  791.      kids = [self children];
  792.      if ([kids count] < 1)
  793.      {  dirtyBoundingBox = NO;
  794.         return self;
  795.      }
  796.  
  797.      // note that it's fair to assume that if we have children, each one has a bounding
  798.      // box.  If it didn't have a bounding box, that would mean it was a leaf node of
  799.      // the tree without any geometry, which means we've got a moot node.
  800.      child = [kids objectAt:0];
  801.  
  802.      // now, we can't just copy this bounding box, because it needs to
  803.      // be transformed by the 4x4 that represents the transformation from this
  804.      // shape to the child, i.e. it's 4x4.  
  805.      N3D_CopyBound(*([child boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]), tmpBoundingBox);
  806.  
  807.      WW3DDetermineBoundGivenBoundAndCTM(&boundingBox, &tmpBoundingBox, tmpCTM);
  808.  
  809.      startingChildIndex = 1;
  810.   }
  811.   else
  812.   {  N3D_CopyBound(*([newAttributeState boundingBox]), boundingBox);
  813.   }
  814.  
  815.   // at this point, we only have a bounding box for the geometry
  816.   // associated with this shape's geometry.  We now need to recurse down
  817.   // our children, asking each to calculate it's bounding box.  We then
  818.   // compare that bounding box with our own, and update ours accordingly.
  819.   // note that if we didn't have any geometry in this shape, we've already
  820.   // looked at the boundingBox of our first child, and set our boundingBox
  821.   // to that, so startingChildIndex has been incremented to 1, so we
  822.   // don't waste time redoing that.
  823.   kids = [self children];
  824.   howManyKids = [kids count];
  825.   for (i = startingChildIndex; i < howManyKids; i++)
  826.   {  child = [kids objectAt:i];
  827.      N3D_CopyBound(*([child boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]), tmpBoundingBox);
  828.  
  829.      // the boundingBox that we get from the child should already be transformed 
  830.      // correctly, right?  Or do we also need to concat the child's transform
  831.      // onto it?  Yes, I think we do need to.  We get the bound, convert it to points,
  832.      // transform it against the child's CTM, then multiply *that* by the
  833.      // tmpCTM (which takes into account transformational RIBCommands in this
  834.      // shape, which will get executed before the child shape, and then
  835.      // convert those points back to a bound.  We can check that against 
  836.      // the current bound.
  837.  
  838.      // we need to transform that bounding box to reflect the transformational 
  839.      // commands that precede it in this shape's ribCommandList
  840.      [child getTransformMatrix:childMatrix];
  841.      WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, &tmpBoundingBox, childMatrix);
  842.      WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, &tmpBoundingBox, tmpCTM);
  843.  
  844.      // now we should all be in the same space; compare against current boundingBox
  845.      //// X
  846.      if (tmpBoundingBox[0] < boundingBox[0])
  847.      {  boundingBox[0] = tmpBoundingBox[0];
  848.      }
  849.      if (tmpBoundingBox[1] > boundingBox[1])
  850.      {  boundingBox[1] = tmpBoundingBox[1];
  851.      }
  852.      //// Y
  853.      if (tmpBoundingBox[2] < boundingBox[2])
  854.      {  boundingBox[2] = tmpBoundingBox[2];
  855.      }
  856.      if (tmpBoundingBox[3] > boundingBox[3])
  857.      {  boundingBox[3] = tmpBoundingBox[3];
  858.      }
  859.      //// Z
  860.      if (tmpBoundingBox[4] < boundingBox[4])
  861.      {  boundingBox[4] = tmpBoundingBox[4];
  862.      }
  863.      if (tmpBoundingBox[5] > boundingBox[5])
  864.      {  boundingBox[5] = tmpBoundingBox[5];
  865.      }
  866.   }
  867.  
  868.   dirtyBoundingBox = NO;
  869.  
  870.   return self;
  871. }
  872.  
  873. // this is a private method, should only used by an instance on itself
  874. - setBoundingBox:(RtBound *)newBoundingBox { return self; }
  875.  
  876.  
  877. - (BOOL)matricesAreEqual:(RtMatrix)m1 :(RtMatrix)m2
  878. {
  879.   if (m1[0][0] != m2[0][0])  {  return NO; }
  880.   if (m1[0][1] != m2[0][1])  {  return NO; }
  881.   if (m1[0][2] != m2[0][2])  {  return NO; }
  882.   if (m1[0][3] != m2[0][3])  {  return NO; }
  883.  
  884.   if (m1[1][0] != m2[1][0])  {  return NO; }
  885.   if (m1[1][1] != m2[1][1])  {  return NO; }
  886.   if (m1[1][2] != m2[1][2])  {  return NO; }
  887.   if (m1[1][3] != m2[1][3])  {  return NO; }
  888.  
  889.   if (m1[2][0] != m2[2][0])  {  return NO; }
  890.   if (m1[2][1] != m2[2][1])  {  return NO; }
  891.   if (m1[2][2] != m2[2][2])  {  return NO; }
  892.   if (m1[2][3] != m2[2][3])  {  return NO; }
  893.  
  894.   if (m1[3][0] != m2[3][0])  {  return NO; }
  895.   if (m1[3][1] != m2[3][1])  {  return NO; }
  896.   if (m1[3][2] != m2[3][2])  {  return NO; }
  897.   if (m1[3][3] != m2[3][3])  {  return NO; }
  898.  
  899.   return YES;
  900. }
  901.  
  902.  
  903. - (BOOL)isIdentityMatrix:(RtMatrix)m  { return [self matricesAreEqual:(RtFloat (*)[4])N3DIdentityMatrix :m]; }
  904.  
  905. - writeEve:(NXStream *)stream atTabLevel:(int)tab
  906. {
  907.    RtMatrix  aMatrix;
  908.    int       i, howManySpaces, howMany = [ribCommandList count];
  909.    id        aCmd;
  910.  
  911.  
  912.    for (i = 0; i < tab; i++)
  913.    {  NXPrintf(stream, "\t");
  914.    }
  915.    NXPrintf(stream, "startShape {%s} ", [self shapeName]); 
  916.    [self getTransformMatrix:aMatrix];
  917.    if ([self isIdentityMatrix:aMatrix])
  918.    {  NXPrintf(stream, ";\n");
  919.    }
  920.    else  // need to add the transformation matrix, since it's not the identity matrix...
  921.    {  NXPrintf(stream, "{");
  922.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  923.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  924.       // it would be nice if we put these on separate lines, but line up...
  925.       // how far in do we need to go? well, first we need to tab the right amount, and then
  926.       // move over "startShape " + strlen([self shapeName]) + "{"....
  927.       for (i = 0; i < tab; i++)
  928.       {  NXPrintf(stream, "\t");
  929.       }
  930.       howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  931.  
  932.       for (i = 0; i < howManySpaces; i++)
  933.       {  NXPrintf(stream, " ");
  934.       }
  935.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  936.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  937.    
  938.       for (i = 0; i < tab; i++)
  939.       {  NXPrintf(stream, "\t");
  940.       }
  941.       for (i = 0; i < howManySpaces; i++)
  942.       {  NXPrintf(stream, " ");
  943.       }
  944.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  945.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  946.  
  947.       for (i = 0; i < tab; i++)
  948.       {  NXPrintf(stream, "\t");
  949.       }
  950.       for (i = 0; i < howManySpaces; i++)
  951.       {  NXPrintf(stream, " ");
  952.       }
  953.       NXPrintf(stream, "%f %f %f %f};\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  954.       NXPrintf(stream, "\n");
  955.    }
  956.    // WAVE!!! Need to write out the rest of my shaders here!!!
  957.    if (surfaceShader)
  958.    {  [surfaceShader writeEve:stream atTabLevel:(tab + 1)];
  959.       NXPrintf(stream, "\n");
  960.    }
  961.  
  962.    // okay, now tell all my rib commands to write themselves out...
  963.    for (i = 0; i < howMany; i++)
  964.    {  aCmd = [ribCommandList objectAt:i];
  965.       [aCmd writeEve:stream atTabLevel:(tab+1)];
  966.       NXPrintf(stream, "\n");
  967.    }
  968.  
  969.    // now tell my descendant to writeEve: itself...
  970.    [descendant writeEve:stream atTabLevel:(tab + 1)];
  971.    NXPrintf(stream, "\n");
  972.  
  973.    for (i = 0; i < tab; i++)
  974.    {  NXPrintf(stream, "\t");
  975.    }
  976.    NXPrintf(stream, "endShape\n"); 
  977.  
  978.    // now tell my nextPeer to writeEve: itself...
  979.    [nextPeer writeEve:stream atTabLevel:tab];
  980.  
  981.    return self;
  982. }
  983.  
  984. - writeScene:(NXStream *)stream atTabLevel:(int)tab
  985. {
  986.    RtMatrix  aMatrix;
  987.    int       i, howManySpaces, howMany = [ribCommandList count];
  988.    id        aCmd;
  989.  
  990.  
  991.    for (i = 0; i < tab; i++)
  992.    {  NXPrintf(stream, "\t");
  993.    }
  994.    NXPrintf(stream, "startShape {%s} ", [self shapeName]); 
  995.    [self getTransformMatrix:aMatrix];
  996.    if ([self isIdentityMatrix:aMatrix])
  997.    {  NXPrintf(stream, ";\n");
  998.    }
  999.    else  // need to add the transformation matrix, since it's not the identity matrix...
  1000.    {  NXPrintf(stream, "{");
  1001.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1002.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1003.       // it would be nice if we put these on separate lines, but line up...
  1004.       // how far in do we need to go? well, first we need to tab the right amount, and then
  1005.       // move over "startShape " + strlen([self shapeName]) + "{"....
  1006.       for (i = 0; i < tab; i++)
  1007.       {  NXPrintf(stream, "\t");
  1008.       }
  1009.       howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  1010.  
  1011.       for (i = 0; i < howManySpaces; i++)
  1012.       {  NXPrintf(stream, " ");
  1013.       }
  1014.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1015.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1016.    
  1017.       for (i = 0; i < tab; i++)
  1018.       {  NXPrintf(stream, "\t");
  1019.       }
  1020.       for (i = 0; i < howManySpaces; i++)
  1021.       {  NXPrintf(stream, " ");
  1022.       }
  1023.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1024.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1025.  
  1026.       for (i = 0; i < tab; i++)
  1027.       {  NXPrintf(stream, "\t");
  1028.       }
  1029.       for (i = 0; i < howManySpaces; i++)
  1030.       {  NXPrintf(stream, " ");
  1031.       }
  1032.       NXPrintf(stream, "%f %f %f %f};\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1033.       NXPrintf(stream, "\n");
  1034.    }
  1035.    // WAVE!!! Need to write out the rest of my shaders here!!!
  1036.    if (surfaceShader)
  1037.    {  [surfaceShader writeScene:stream atTabLevel:(tab + 1)];
  1038.       NXPrintf(stream, "\n");
  1039.    }
  1040.  
  1041.    // okay, now tell all my rib commands to write themselves out...
  1042.    for (i = 0; i < howMany; i++)
  1043.    {  aCmd = [ribCommandList objectAt:i];
  1044.       [aCmd writeScene:stream atTabLevel:(tab+1)];
  1045.       NXPrintf(stream, "\n");
  1046.    }
  1047.  
  1048.    // now tell my descendant to writeScene: itself...
  1049.    [descendant writeScene:stream atTabLevel:(tab + 1)];
  1050.    NXPrintf(stream, "\n");
  1051.  
  1052.    for (i = 0; i < tab; i++)
  1053.    {  NXPrintf(stream, "\t");
  1054.    }
  1055.    NXPrintf(stream, "endShape\n"); 
  1056.  
  1057.    // now tell my nextPeer to writeScene: itself...
  1058.    [nextPeer writeScene:stream atTabLevel:tab];
  1059.  
  1060.    return self;
  1061. }
  1062.  
  1063.  
  1064. - write3DTextScene:(NXStream *)stream atTabLevel:(int)tab index:(int)index time:(float)time until:(float)lastTime
  1065. {
  1066.    RtMatrix  aMatrix;
  1067.    int       i, 
  1068.              howManySpaces, howMany = [ribCommandList count];
  1069.    id        aCmd;
  1070.  
  1071.  
  1072.    for (i = 0; i < tab; i++)
  1073.    {  NXPrintf(stream, "\t");
  1074.    }
  1075.  
  1076.    NXPrintf(stream, "startShape WW3DShape; ");
  1077.    // need tab
  1078.    // need index (position in current list)
  1079.    NXPrintf(stream, 
  1080.         "EveCmd {Translate [expr { %d * $__text__(tabLength)}] [expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] 0 };\n", 
  1081.             tab, index); 
  1082.    for (i = 0; i < tab; i++)
  1083.    {  NXPrintf(stream, "\t");
  1084.    }
  1085.    NXPrintf(stream, "  EveCmd {WW3DText $__text__(fontName) $__text__(fontSize) {");
  1086.      NXPrintf(stream, "startShape %s ", [self shapeName]); 
  1087.      [self getTransformMatrix:aMatrix];
  1088.      if ([self isIdentityMatrix:aMatrix])
  1089.      {  NXPrintf(stream, " } left;\n");
  1090.      }
  1091.      else  // need to add the transformation matrix, since it's not the identity matrix...
  1092.      {  NXPrintf(stream, "{");
  1093.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1094.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1095.         // it would be nice if we put these on separate lines, but line up...
  1096.         // how far in do we need to go? well, first we need to tab the right amount, and then
  1097.         // move over "startShape " + strlen([self shapeName]) + "{"....
  1098.         for (i = 0; i < tab; i++)
  1099.         {  NXPrintf(stream, "\t");
  1100.         }
  1101.         howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  1102.   
  1103.         for (i = 0; i < howManySpaces; i++)
  1104.         {  NXPrintf(stream, " ");
  1105.         }
  1106.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1107.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1108.  
  1109.         for (i = 0; i < tab; i++)
  1110.         {  NXPrintf(stream, "\t");
  1111.         }
  1112.         for (i = 0; i < howManySpaces; i++)
  1113.         {  NXPrintf(stream, " ");
  1114.         }
  1115.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1116.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1117.   
  1118.         for (i = 0; i < tab; i++)
  1119.         {  NXPrintf(stream, "\t");
  1120.         }
  1121.         for (i = 0; i < howManySpaces; i++)
  1122.         {  NXPrintf(stream, " ");
  1123.         }
  1124.         NXPrintf(stream, "%f %f %f %f}} left;\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1125.         NXPrintf(stream, "\n");
  1126.      }
  1127.    NXPrintf(stream, "}\n");
  1128.   
  1129.    index++;
  1130.    // okay, next object - it will be a child of this shape:
  1131.    if (surfaceShader)
  1132.    {  [surfaceShader write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1133.       NXPrintf(stream, "\n");
  1134.    }
  1135.  
  1136.    // okay, now tell all my rib commands to write themselves out...
  1137.    for (i = 0; i < howMany; i++)
  1138.    {  aCmd = [ribCommandList objectAt:i];
  1139.       [aCmd write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1140.       NXPrintf(stream, "\n");
  1141.    }
  1142.  
  1143.    // now tell my descendant to writeEve: itself...
  1144.    [descendant write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1145.    NXPrintf(stream, "\n");
  1146.  
  1147.    // okay, now finish this text shape off by putting out the endShape
  1148.  
  1149.    for (i = 0; i < tab; i++)
  1150.    {  NXPrintf(stream, "\t");
  1151.    }
  1152.    NXPrintf(stream, "startShape endShape; ");
  1153.    // need tab
  1154.    // need index (position in current list)
  1155.    NXPrintf(stream, 
  1156.         "EveCmd {Translate [expr { %d * $__text__(tabLength)}] [expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] 0 };\n", 
  1157.             tab, index); 
  1158.    NXPrintf(stream, "  EveCmd {WW3DText $__text__(fontName) $__text__(fontSize) {");
  1159.      NXPrintf(stream, "endShape");
  1160.    NXPrintf(stream, "} left;}\n");
  1161.  
  1162.    for (i = 0; i < tab; i++)
  1163.    {  NXPrintf(stream, "\t");
  1164.    }
  1165.   NXPrintf(stream, "endShape;\n");
  1166.  
  1167.    for (i = 0; i < tab; i++)
  1168.    {  NXPrintf(stream, "\t");
  1169.    }
  1170.   NXPrintf(stream, "endShape;\n");  // okay, end off the WW3DShape
  1171.  
  1172.  
  1173.    // now tell my nextPeer to writeEve: itself...
  1174.    [nextPeer write3DTextScene:stream atTabLevel:(tab+1) index:index time:time until:lastTime];
  1175.  
  1176.    return self;
  1177. }
  1178.  
  1179.  
  1180. - writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
  1181. {
  1182.    RtMatrix  aMatrix;
  1183.    int       i, newTab, howMany = [ribCommandList count];
  1184.    id        aCmd;
  1185.  
  1186.  
  1187.    for (i = 0; i < tab; i++)
  1188.    {  NXPrintf(stream, "\t");
  1189.    }
  1190.    NXPrintf(stream, "# startShape {%s}\n", [self shapeName]);
  1191.    for (i = 0; i < tab; i++)
  1192.    {  NXPrintf(stream, "\t");
  1193.    }
  1194.    NXPrintf(stream, "DEF \"%s\" Separator {\n", [self shapeName]);
  1195.    [self getTransformMatrix:aMatrix];
  1196.    if (![self isIdentityMatrix:aMatrix])
  1197.    {  
  1198.  
  1199.       for (i = 0; i < (tab+1); i++)
  1200.       {  NXPrintf(stream, "\t");
  1201.       }
  1202.       NXPrintf(stream, "MatrixTransform {\n");
  1203.       for (i = 0; i < (tab+2); i++)
  1204.       {  NXPrintf(stream, "\t");
  1205.       }
  1206.       NXPrintf(stream, "matrix %f %f %f %f\n", aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1207.       for (i = 0; i < (tab+2); i++)
  1208.       {  NXPrintf(stream, "\t");
  1209.       }
  1210.       NXPrintf(stream, "       %f %f %f %f\n",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1211.       for (i = 0; i < (tab+2); i++)
  1212.       {  NXPrintf(stream, "\t");
  1213.       }
  1214.       NXPrintf(stream, "       %f %f %f %f\n",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1215.       for (i = 0; i < (tab+2); i++)
  1216.       {  NXPrintf(stream, "\t");
  1217.       }
  1218.       NXPrintf(stream, "       %f %f %f %f\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1219.       for (i = 0; i < (tab+1); i++)
  1220.       {  NXPrintf(stream, "\t");
  1221.       }
  1222.       NXPrintf(stream, "}\n");
  1223.    }
  1224.    // WAVE!!! Need to write out the rest of my shaders here!!!
  1225.    if (surfaceShader)
  1226.    {  [surfaceShader writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1227.       NXPrintf(stream, "\n");
  1228.    }
  1229.    if (displacementShader)
  1230.    {  [displacementShader writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1231.       NXPrintf(stream, "\n");
  1232.    }
  1233.  
  1234.    // okay, now tell all my rib commands to write themselves out...
  1235.    newTab = tab + 1;
  1236.    for (i = 0; i < howMany; i++)
  1237.    {  aCmd = [ribCommandList objectAt:i];
  1238.       if (![aCmd isMootStartingAt:currentTime endingAt:currentTime])
  1239.       {  if ([aCmd popsCTM])
  1240.          {  newTab--;
  1241.          }
  1242.          [aCmd writeInventorAtTime:currentTime to:stream atTabLevel:newTab];
  1243.          if ([aCmd pushesCTM])
  1244.          {  newTab++;
  1245.          }
  1246.          NXPrintf(stream, "\n");
  1247.       }
  1248.    }
  1249.  
  1250.    // now tell my descendant to writeInventor: itself...
  1251.    aCmd = [ribCommandList objectAt:i];
  1252.    if (![aCmd isMootStartingAt:currentTime endingAt:currentTime])
  1253.    {  [descendant writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1254.       NXPrintf(stream, "\n");
  1255.    }
  1256.  
  1257.    for (i = 0; i < tab; i++)
  1258.    {  NXPrintf(stream, "\t");
  1259.    }
  1260.    NXPrintf(stream, "}\n"); 
  1261.  
  1262.    // now tell my nextPeer to writeInventor: itself...
  1263.    [nextPeer writeInventorAtTime:currentTime to:stream atTabLevel:tab];
  1264.  
  1265.    return self;
  1266. }
  1267.  
  1268.  
  1269. #define typeVector "fcfffffffff"
  1270. #define typeValues &visibleSizeScale, &visibleWhenPrinting, \
  1271.                    &a, &b, &c, &d, &vMagnitude, &cosOfAlpha, &sinOfAlpha, &xRotate, &yRotate
  1272.            
  1273. - read:(NXTypedStream *)stream 
  1274. {
  1275.     int    version;
  1276.     
  1277.     [super read:stream];
  1278.     
  1279.     NX_DURING
  1280.     version = NXTypedStreamClassVersion(stream, "WW3DLight");
  1281.     if (version == 1) 
  1282.         {  NXReadTypes(stream, typeVector, typeValues);
  1283.        NXReadArray(stream, "f", 3, v);
  1284.        NXReadArray(stream, "f", 3, u);
  1285.        NXReadArray(stream, "f", 3, uPrime);
  1286.        NXReadArray(stream, "f", 3, innerConeOpacity);
  1287.        NXReadArray(stream, "f", 3, outerConeOpacity);
  1288.        NXReadArray(stream, "f", 3, beamOpacity);
  1289.     }
  1290.     NX_HANDLER
  1291.     NXLogError("in read: %s, exception [%d] raised.\n", [[self class] name], NXLocalHandler.code);
  1292.     return nil;
  1293.     NX_ENDHANDLER
  1294.     return self;
  1295. }
  1296.  
  1297. - write:(NXTypedStream *)stream 
  1298. {
  1299.     [super write:stream];
  1300.     NXWriteTypes(stream, typeVector, typeValues);
  1301.     NXWriteArray(stream, "f", 3, v);
  1302.     NXWriteArray(stream, "f", 3, u);
  1303.     NXWriteArray(stream, "f", 3, uPrime);
  1304.     NXWriteArray(stream, "f", 3, innerConeOpacity);
  1305.     NXWriteArray(stream, "f", 3, outerConeOpacity);
  1306.     NXWriteArray(stream, "f", 3, beamOpacity);
  1307.     return self;
  1308. }
  1309.  
  1310. // boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
  1311. - class { return [super class]; }
  1312.  
  1313. @end
  1314.